package com.atguigu.yygh.hosp.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.yygh.common.exception.YyghException;
import com.atguigu.yygh.hosp.respository.DepartmentRepository;
import com.atguigu.yygh.hosp.respository.HospitalRepository;
import com.atguigu.yygh.hosp.respository.ScheduleRepository;
import com.atguigu.yygh.hosp.service.DepartmentService;
import com.atguigu.yygh.hosp.service.ScheduleService;
import com.atguigu.yygh.model.hosp.BookingRule;
import com.atguigu.yygh.model.hosp.Department;
import com.atguigu.yygh.model.hosp.Hospital;
import com.atguigu.yygh.model.hosp.Schedule;
import com.atguigu.yygh.vo.hosp.BookingScheduleRuleVo;
import com.atguigu.yygh.vo.hosp.ScheduleOrderVo;
import org.joda.time.DateTime;
import org.joda.time.DateTimeConstants;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author luoyin
 * @Date 19:53 2022/9/27
 **/
@Service
public class ScheduleServiceImpl implements ScheduleService {
    @Autowired
    private ScheduleRepository scheduleRepository;

    @Autowired
    private HospitalRepository hospitalRepository;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private DepartmentService departmentService;

    @Override
    public void save(Map<String, String> paramMap) {
        Schedule schedule = JSONObject.parseObject(JSONObject.toJSONString(paramMap), Schedule.class);
        Schedule sch = new Schedule();
        sch.setHoscode(schedule.getHoscode());
        sch.setDepcode(schedule.getDepcode());
        sch.setHosScheduleId(schedule.getHosScheduleId());
        Example<Schedule> example = Example.of(sch);
        Optional<Schedule> optional = scheduleRepository.findOne(example);
        if(optional.isPresent()){
            //存在排班信息,修改
            Schedule op = optional.get();
            schedule.setId(op.getId());
            schedule.setCreateTime(op.getCreateTime());
            schedule.setUpdateTime(new Date());
            schedule.setIsDeleted(op.getIsDeleted());

        }else {
            //不存在,添加
            schedule.setCreateTime(new Date());
            schedule.setUpdateTime(new Date());
            schedule.setIsDeleted(0);
        }
        scheduleRepository.save(schedule);
    }

    @Override
    public Page<Schedule> findSchedulePage(Map<String, String> paramMap) {
        //查询那个医院
        Schedule schedule = new Schedule();
        schedule.setHoscode(paramMap.get("hoscode"));
        schedule.setIsDeleted(0);
        Example<Schedule> example = Example.of(schedule);

        //分页 第一页是0
        PageRequest pageRequest = PageRequest.of(Integer.parseInt(paramMap.get("page"))-1, Integer.parseInt(paramMap.get("limit")));
        return scheduleRepository.findAll(example, pageRequest);
    }

    @Override
    public void remove(Map<String, String> paramMap) {
        //根据医院编码和排班id查询
        Schedule sch = new Schedule();
        sch.setHoscode(paramMap.get("hoscode"));
        sch.setHosScheduleId(paramMap.get("hosScheduleId"));
        sch.setIsDeleted(0);
        Example<Schedule> example = Example.of(sch);
        Optional<Schedule> optional = scheduleRepository.findOne(example);
        if(!optional.isPresent()){
            throw new YyghException(201,"排班信息不存在");
        }
        Schedule schedule = optional.get();
        //0 未删除  1 已删除
        schedule.setIsDeleted(1);
        scheduleRepository.save(schedule);
    }

    @Override
    public Map<String, Object> getScheduleRule(Integer pageNum, Integer pageSize, String hoscode, String depcode) {
        Criteria criteria = Criteria.where("hoscode").is(hoscode).and("depcode").is(depcode);
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.group("workDate")
                        .first("workDate").as("workDate")
                        .sum("reservedNumber").as("reservedNumber")
                        .sum("availableNumber").as("availableNumber"),
                Aggregation.sort(Sort.by(Sort.Order.asc("workDate"))),
                Aggregation.skip( (pageNum - 1)*pageSize),
                Aggregation.limit(pageSize));
        AggregationResults<BookingScheduleRuleVo> bookingScheduleRuleVos = mongoTemplate.aggregate(aggregation, Schedule.class, BookingScheduleRuleVo.class);
        List<BookingScheduleRuleVo> results = bookingScheduleRuleVos.getMappedResults();
        for (BookingScheduleRuleVo result : results) {
            Date workDate = result.getWorkDate();
            String dayOfWeek = getDayOfWeek(new DateTime(workDate));
            result.setDayOfWeek(dayOfWeek);
        }

        //查询总记录数
        Aggregation aggregation2 = Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.group("workDate")
                        .first("workDate").as("workDate"));
        AggregationResults<BookingScheduleRuleVo> bookingScheduleRuleVos2 = mongoTemplate.aggregate(aggregation2, Schedule.class, BookingScheduleRuleVo.class);
        List<BookingScheduleRuleVo> results2= bookingScheduleRuleVos2.getMappedResults();
        Map<String, Object> map = new HashMap<>();
        int total = results2.size();
        map.put("list",results);
        map.put("total",total);
        return map;
    }

    @Override
    public List<Schedule> getScheduleDetail(String hoscode, String depcode, Date workDate) {
        //查询条件
        Schedule sch = new Schedule();
        sch.setIsDeleted(0);
        sch.setHoscode(hoscode);
        sch.setDepcode(depcode);
        sch.setWorkDate(workDate);
        Example<Schedule> example = Example.of(sch);
        return scheduleRepository.findAll(example);
    }

    @Override
    public Map<String, Object> selectPage(Integer pageNum, Integer pageSize, String hoscode, String depcode) {

        Hospital hospital = hospitalRepository.getByHoscode(hoscode);
        if(hospital == null){
            throw new YyghException(20001,"医院不存在!");
        }
        BookingRule bookingRule = hospital.getBookingRule();

        //获取连续的分页信息列表
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<Date> page = getDateList(pageNum,pageSize,bookingRule);
        //当前页的连续的日期列表
        List<Date> records = page.getRecords();
        //分组查询 , 条件: 在日期列表 records 中 hoscode depcode 排序
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(Criteria.where("hoscode").is(hoscode).and("depcode").is(depcode).and("workDate").in(records)),
                Aggregation.group("workDate")
                        .first("workDate").as("workDate")
                        .sum("reservedNumber").as("reservedNumber")
                        .sum("availableNumber").as("availableNumber"),
                Aggregation.sort(Sort.by(Sort.Order.asc("workDate")))
        );
        //在date 列表中的所有数据
        AggregationResults<BookingScheduleRuleVo> aggregate = mongoTemplate.aggregate(aggregation, Schedule.class, BookingScheduleRuleVo.class);
        List<BookingScheduleRuleVo> results = aggregate.getMappedResults();
        //把列表转换成map,好查询
        Map<Date, BookingScheduleRuleVo> ruleVoMap = results.stream().collect(Collectors.toMap(BookingScheduleRuleVo::getWorkDate, bookingScheduleRuleVo -> bookingScheduleRuleVo));

        //新建一个列表,把两个列表合在一起
        List<BookingScheduleRuleVo> ruleVos = new ArrayList<>();
        for (int i = 0; i < records.size(); i++) {
            BookingScheduleRuleVo vo = ruleVoMap.get(records.get(i));
            if(vo == null){
               //不在日期列表
                vo = new BookingScheduleRuleVo();
                vo.setWorkDate(records.get(i));
                vo.setAvailableNumber(-1);
                vo.setReservedNumber(0);
           }
            vo.setDayOfWeek(getDayOfWeek(new DateTime(records.get(i))));
            vo.setStatus(0);
            //是否是第一页的第一个,如果是,超过预约时间,停止挂号
            if(i == 0 && pageNum == 1 && parseDate(new Date() , bookingRule.getStopTime()).isBeforeNow()){
                vo.setStatus(-1);
            }
            //是否是最后一页的最后一个,如果是,即将预约
            if( pageNum == page.getPages() && i == records.size() - 1){
                vo.setStatus(1);
            }
            ruleVos.add(vo);
        }

        HashMap<String, Object> map = new HashMap<>();
        HashMap<String, Object> baseMap = new HashMap<>();
        baseMap.put("hosname",hospital.getHosname());
        Department department = departmentService.getDepartment(hoscode,depcode);
        baseMap.put("bigname",department.getBigname());
        baseMap.put("depname",department.getDepname());
        baseMap.put("workDateString",new DateTime().toString("yyyy年MM月"));
        baseMap.put("releaseTime",bookingRule.getReleaseTime());
        baseMap.put("stopTime",bookingRule.getStopTime());
        map.put("list",ruleVos);
        map.put("total",page.getTotal());
        map.put("baseMap",baseMap);
        return map;
    }

    @Override
    public Schedule getSchedule(String scheduleId) {
        Optional<Schedule> optional = scheduleRepository.findById(scheduleId);
        if (!optional.isPresent()){
            throw new YyghException(20001,"不存在该科室");
        }
        Schedule schedule = optional.get();
        Hospital hospital = hospitalRepository.getByHoscode(schedule.getHoscode());
        Department department = departmentService.getDepartment(schedule.getHoscode(), schedule.getDepcode());

        schedule.getParam().put("dayOfWeek",getDayOfWeek(new DateTime(schedule.getWorkDate())));
        schedule.getParam().put("hosname",hospital.getHosname());
        schedule.getParam().put("depname",department.getDepname());
        return schedule;
    }

    @Override
    public ScheduleOrderVo getScheduleOrderVo(String scheduleId) {
        Schedule schedule = getSchedule(scheduleId);
        ScheduleOrderVo scheduleOrderVo = new ScheduleOrderVo();
        BeanUtils.copyProperties(schedule,scheduleOrderVo);
        Hospital hospital = hospitalRepository.getByHoscode(schedule.getHoscode());
        scheduleOrderVo.setHosname(hospital.getHosname());
        scheduleOrderVo.setDepname(schedule.getParam().get("depname").toString());
        scheduleOrderVo.setReserveDate(schedule.getWorkDate());
        scheduleOrderVo.setReserveTime(schedule.getWorkTime());
        BookingRule bookingRule = hospital.getBookingRule();
        DateTime quitTime = new DateTime(schedule.getWorkDate()).plusDays(bookingRule.getQuitDay());
        scheduleOrderVo.setQuitTime(parseDate(quitTime.toDate(), bookingRule.getQuitTime()).toDate());
        String stopTime = bookingRule.getStopTime();
        //当天停止挂号时间
        scheduleOrderVo.setStopTime(parseDate(schedule.getWorkDate(),stopTime).toDate());
        //挂号结束时间
        scheduleOrderVo.setEndTime(parseDate(new DateTime().plusDays(bookingRule.getCycle()).toDate(),stopTime).toDate());
        return scheduleOrderVo;
    }

    @Override
    public void update(Schedule schedule) {
        schedule.setUpdateTime(new Date());
        scheduleRepository.save(schedule);
    }

    @Override
    public Schedule selectSchedule(String hoscode, String depcode, String hosScheduleId) {
        return scheduleRepository.getScheduleByHoscodeAndDepcodeAndHosScheduleId(hoscode,depcode,hosScheduleId);
    }

    private com.baomidou.mybatisplus.extension.plugins.pagination.Page<Date> getDateList(Integer pageNum, Integer pageSize,BookingRule bookingRule) {

        //预约周期
        Integer cycle = bookingRule.getCycle();

        //放号时间
        DateTime startTime = parseDate( new Date() , bookingRule.getReleaseTime() );

        //判断有没有过开始预约时间,如果有,预约周期加1
        if (startTime.isBeforeNow()){
            //现在时间在放号时间后面
            cycle = cycle + 1;
        }
        //获取当前页数Date列表
        int start = (pageNum - 1)*pageSize;
        //判断结束个数是否大于总个数
        int end = (start + pageSize) > cycle ? cycle : (start + pageSize) ;
        List<Date> list = new ArrayList<>();
        for (int i = start ; i < end ; i++){
            String time = new DateTime().plusDays(i).toString("yyyy-MM-dd");
            list.add(new DateTime(time).toDate());
        }
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<Date> page = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(pageNum,pageSize,cycle);
        page.setRecords(list);
        return page;
    }

    private DateTime parseDate(Date date, String releaseTime) {
        String time = new DateTime(date).toString("yyyy-MM-dd") + " " + releaseTime ;
        return DateTimeFormat.forPattern("yyyy-MM-dd HH:mm").parseDateTime(time);
    }

    private String getDayOfWeek(DateTime dateTime){
        String dayOfWeek = null;
        switch (dateTime.getDayOfWeek()){
            case DateTimeConstants.SUNDAY:
                dayOfWeek = "星期天";
                break;
            case DateTimeConstants.MONDAY:
                dayOfWeek = "星期一";
                break;
            case DateTimeConstants.TUESDAY:
                dayOfWeek = "星期二";
                break;
            case DateTimeConstants.WEDNESDAY:
                dayOfWeek = "星期三";
                break;
            case DateTimeConstants.THURSDAY:
                dayOfWeek = "星期四";
                break;
            case DateTimeConstants.FRIDAY:
                dayOfWeek = "星期五";
                break;
            case DateTimeConstants.SATURDAY:
                dayOfWeek = "星期六";
                break;
        }
        return dayOfWeek;
    }
}
